package simtester.server

import java.text.DateFormat;
import java.text.SimpleDateFormat

class TestRunService {
    static Random rand = new Random()
    static private final DateFormat dateFormat = new SimpleDateFormat('MM/dd/yyyy HH:mm:ss.SSS')
    
    static transactional = false

    def saveTestRunResults(testrunId, results) {
        results.eachLine { line ->
            def info = extractInfoFrom(line)
            
            if (info.tc) {
                info.tc = info.tc.toLong()
            }
            
            if (info.startnum) {
                def iterationParams = [user: info.user, num: info.startnum, status: 'running', startDate: info.timestamp]
                if (info.tc) {
                    iterationParams.testcase = info.tc
                }

                retryTestRunSave {
                    Iteration iter = new Iteration(iterationParams)
                    TestRun tr = TestRun.get(testrunId)
                    tr.addToIterations(iter)
                    tr.save(flush: true, failOnError: true)
                }
            } else if (info.endnum) {
                retryTestRunSave { session ->
                    Iteration iter
                    boolean foundIter = false
                    while (!foundIter) {
                        TestRun tr = TestRun.get(testrunId)
                        if (info.tc) {
                            iter = tr.iterations.find { it.testcase == info.tc && it.user == info.user && it.num == info.endnum }
                        } else {
                            iter = tr.iterations.find { it.user == info.user && it.num == info.endnum }
                        }
                        
                        if (iter) {
                            foundIter = true
                        } else {
                            println "Trying to end iter. Not found: tc: $info.tc, user: $info.user, num: $info.endnum"
                            session.clear()
                        }
                    }
                    iter.endDate = info.timestamp
                    iter.save(flush: true)
                }
            } else if (info.iterStatus) {
                info.iter = info.iter.toInteger()
                info.user = info.user.toInteger()
                
                retryTestRunSave { session ->
                    Iteration iter
                    boolean foundIter = false
                    while (!foundIter) {
                        TestRun tr = TestRun.get(testrunId)
                        if (info.tc) {
                            iter = tr.iterations.find { it.testcase == info.tc && it.user == info.user && it.num == info.iter }
                        } else {
                            iter = tr.iterations.find { it.user == info.user && it.num == info.iter }
                        }
                        
                        if (iter) {
                            foundIter = true
                        } else {
                            println "Trying to change status. Not found: tc: $info.tc, user: $info.user, num: $info.iter"
                            session.clear()
                        }
                    }
                    iter.status = info.iterStatus
                    iter.save(flush: true)
                }
            } else {
                ResultEntry resultEntry = new ResultEntry(testrun:   TestRun.get(testrunId),
                                                          timestamp: info.timestamp,
                                                          level:     info.level,
                                                          type:      info.type,
                                                          response:  info.resp,
                                                          entry:     info.entry,
                                                          user:      info.user,
                                                          iteration: info.iter)
                
                if (info.tc) {
                    resultEntry.testcase = info.tc.toLong()
                }
                
                //saveLog(testrunId, resultEntry)
                resultEntry.save()
            }
        }
    }
    
    /*def saveLog(ObjectId id, ResultEntry resultEntry) {
        retryTestRunSave {
            TestRun tr = TestRun.get(id)
            tr.addToResults(resultEntry)
            tr.save(flush: true)
        }
    }*/
    
    def retryTestRunSave(Closure closure) {
        boolean saveSuccessful = false
        while (!saveSuccessful) {
            TestRun.withSession { session ->
                try {
                    closure.call(session)
                    saveSuccessful = true
                } catch (org.springframework.dao.OptimisticLockingFailureException e) {
                    println "retryTestRunSave: Lock Exception: $e.message"
                    def time = rand.nextInt(5) * 50
                    sleep time
                    session.clear()
                }
            }
        }
    }
    
    private def extractInfoFrom(str) {
        // Example str's:
        // 02/04/2012 02:40:54.912 INFO: resp:128 type:GET|http://www.google.com
        // 02/04/2012 02:40:54.912 INFO: Title: Google

        //               1                                         2         3        4
        def m = str =~ /(\d\d\/\d\d\/\d{4} \d\d:\d\d:\d\d\.\d{3}) (\w+): (?:(.*?)\|)?(.+)$/
        m = m[0]

        def info = [:]
        DateFormat format = (DateFormat)dateFormat.clone()
        info.timestamp = format.parse(m[1])
        info.level = m[2]
        info.entry = m[4]
        
        // Start of test iteration
        def match = info.entry =~ /----- Start Test Run (.+) -----/
        if (match) {
            def attrs = match[0][1].tokenize(':')
            info.user     = attrs[0].toInteger()
            info.startnum = attrs[1].toInteger()
            if (attrs[2]) {
                info.tc = attrs[2]
            }
        }
        
        // End of test iteration
        match = info.entry =~ /----- End Test Run (.+) -----/
        if (match) {
            def attrs = match[0][1].tokenize(':')
            info.user   = attrs[0].toInteger()
            info.endnum = attrs[1].toInteger()
            if (attrs[2]) {
                info.tc = attrs[2]
            }
        }

        // Change status of iteration
        match = info.entry =~ /SYS:testrun status:(.+)/
        if (match) {
            info.iterStatus = match[0][1]
        }
        
        info.entry = info.entry.replaceAll("\\\\n", "\n")
        
        if (!m[3]) {
            return info
        }

        m[3].split(' ').each { attr ->
            def attrAndValue = attr.split(':')
            info[attrAndValue[0]] = attrAndValue[1]
        }
        
        if (info.resp) {
            info.resp = info.resp.toInteger()
        }

        return info
    }
}
